---
title: Workflow triggers
description: Learn how to use webhooks and schedules to trigger workflows.
icon: bolt
---

<Note>
  This tutorial does not cover how to expose your Tracecat instance to the public internet.

- If you're running Tracecat locally, you'll need use a tunneling service like [ngrok](https://ngrok.com/) to receive webhook requests from the public internet.
- If you're running Tracecat on AWS Fargate, Tracecat is automatically exposed to the public internet via an Application Load Balancer.
</Note>

## What you'll learn

By the end of this tutorial, you'll learn how to create two types of workflows:

- A webhook workflow that receives a JSON string and decodes it using the `FN.deserialize_json` function.
- A scheduled workflow that sends a notification to a Slack channel at a regular interval.

## Trigger action

Every Tracecat workflow comes with a single `Trigger` action.
This trigger action cannot be deleted.
Click on the `Trigger` action to activate the webhooks and schedules configuration panel.

![Trigger action](/img/tutorials/triggers/trigger-action.png)

### Webhooks

<Warning>
  Webhook URLs are **secrets** and should not be exposed in public. The webhook
  URL shown in the UI screenshots are for demonstration only.
</Warning>

Webhooks are disabled by default. To activate a workflow's webhook, click on the `Trigger` action, then toggle the webhook on.
Events can then be triggered by making a `POST` request to the webhook URL.
Webhook URLs are formatted as:

```bash
WEBHOOK_URL=https://<tracecat-public-url>/api/webhooks/<workflow-id>/<webhook-secret>
```

#### Authenticate webhook requests with API keys

You can lock down a webhook by requiring callers to present an API key.

1. Open the `Trigger` action panel and locate the **API Key** section.
2. Click **Generate API key**. Copy the value when prompted&mdash;it is only
   displayed once.
3. Add the following header to every webhook request:

   ```http
   x-tracecat-api-key: <your-api-key>
   ```

If a request omits the header or uses a revoked key, Tracecat rejects it with an
HTTP `401 Unauthorized` response. You can rotate the key at any time, or revoke
it to temporarily block requests without deleting the configuration.

#### Restrict webhook sources with IP allowlists

Use IP allowlists to accept requests only from trusted networks.

1. In the `Trigger` panel, add one or more IPv4 addresses or CIDR ranges (e.g.
   `203.0.113.7` or `203.0.113.0/24`) to the **IP Allowlist** field.
2. Click **Save** to apply the changes.

Tracecat compares the requester's IP address against the allowlist. Requests
without a client IP or originating from outside the configured ranges are
rejected with an HTTP `403 Forbidden` response. Leave the list empty to allow
traffic from any source.

### Schedules

Schedules are disabled by default. To activate a workflow's schedule, click on the `Trigger` action, then toggle the schedule on.

![Schedule action](/img/tutorials/triggers/schedule.png)

From here you can enter a recurring schedule with a timeout and offset. Tracecat defaults to using UTC time, so it's convenient to use `offset` to work with timezone differences.

For example, if you are in America/New_York timezone and want to run a workflow every day at 9AM, you can configure the schedule to run every 1 day with an `offset` of `PT13H`. This is because 00:00 UTC is 8PM EDT.

![Schedule modal](/img/tutorials/triggers/schedule_modal.png)

### Webhook verification

Some tools (e.g. Slack, Okta) that send webhooks require a specific response after the webhook payload is received.
This is used to verify that the webhook was received and processed by Tracecat.

To echo the webhook payload back to the server, add the query parameter `echo=true` to the webhook URL.

```bash
https://${WEBHOOK_URL}?echo=true
```

By default, we return the workflow context (e.g. `execution_id`) as the response to the webhook POST request.
To return an empty response with status code 200, set the query parameter `empty_echo=true`.

```bash
https://${WEBHOOK_URL}?empty_echo=true
```

### Allowed webhook methods

By default, only `POST` requests are allowed.
To allow other methods, select the trigger action then configure `POST` and / or `GET` in the **Allowed HTTP Methods** section of the action panel.

### Vendor-specific webhooks

<Note>
  Can't find the vendor you're looking for? Open an issue on
  [GitHub](https://github.com/TracecatHQ/tracecat/issues) and we'll add support
  for it.
</Note>

Some vendors have more complex webhook verification requirements.
To support this, Tracecat supports vendor-specific webhook verification.

```bash
https://${WEBHOOK_URL}?vendor=okta
```

Vendors currently supported:

- `okta`: must allow `GET` requests for verification.

### Pause or delete schedule

You can pause or delete the schedule by selecting the schedule menu in the `Trigger` settings panel.

![Pause or delete schedule](/img/tutorials/triggers/pause-delete-schedule.png)

## Reference webhook data

Use the `${{ TRIGGER }}` expression context to reference data passed via webhooks.
A child workflow also receives input data via `${{ TRIGGER }}` context.

![Trigger expression](/img/tutorials/triggers/trigger-expression.png)

Learn more in the [child workflows](/tutorials/child-workflows) and [expressions](/quickstart/expressions#trigger-context) docs.

## Webhook content types

Traceat's webhook parser supports the following `Content-Type` headers:

- `application/json`
- `application/x-ndjson`
- `application/www-form-urlencoded`

<Note>
  The webhook sender must set the `Content-Type` header to the appropriate
  value. If no `Content-Type` header is set, Tracecat defaults to
  `application/json`.
</Note>

### Example: Elastic Security

To ingest alerts from Elastic Security, you'll need to configure their [webhook connector](https://www.elastic.co/docs/reference/kibana/connectors-kibana/webhook-action-type)
in Kibana to send NDJSON with the expected content type (`application/x-ndjson`).

<Accordion title="Step-by-step guide" icon="play">
  <Steps>
    <Step title="Configure Elastic webhook rule action body">
      Configure the body of the webhook connector to return the alerts in NDJSON format:

      ```
      {{#context.alerts}}
      {{{.}}}
      {{/context.alerts}}
      ```
    </Step>
    <Step title="Configure content type">
      The webhook request sent by Elastic Security must have the following header:

      ```json
      {
        "Content-Type": "application/x-ndjson",
      }
      ```
    </Step>
    <Step title="Receive webhook in Tracecat">
      Tracecat looks at the `Content-Type` header to determine how to parse the webhook payload.
      In this case, we're using `application/x-ndjson`, so Tracecat will parse the webhook payload as NDJSON.

      The `${{ TRIGGER }}` expression context will return a list of deserialized JSON objects.

      ![Elastic Security webhook](/img/tutorials/triggers/elastic-security-webhook.png)
    </Step>

  </Steps>
</Accordion>

## Parse webhook payload

<Steps>
  <Step title="Deserialize JSON action">
    Add the `core.transform.reshape` action to the workflow.
    Use the `FN.deserialize_json` function to decode an incoming JSON string via webhook.

    ```yaml
    value: ${{ FN.deserialize_json(TRIGGER.payload)}}
    ```

    ![Deserialize JSON action](/img/tutorials/triggers/deserialize-json.png)

  </Step>
  <Step title="Save workflow">
    Save the workflow.
  </Step>
  <Step title="Toggle webhook">
    Click on the `Trigger` action.
    Enable the webhook by clicking on the toggle switch.
    The workflow will now receive webhook events.

    ![Toggle webhook](/img/tutorials/triggers/toggle-webhook.png)

  </Step>
  <Step title="POST webhook request">
    Copy the webhook URL to your clipboard, then make a `POST` request to the webhook URL.
    For example, using `curl`:

    ```bash
    curl -X POST \
      -H "Content-Type: application/json" \
      -d '{"payload": "{\"name\": \"John\", \"age\": 30}"}' \
      <webhook-url>
    ```

  </Step>
  <Step title="View workflow runs">
    Go to the `Runs` view and check that the workflow has run successfully.
    The workflow should have received the webhook payload and deserialized it into a JSON object.

    ![Deserialize JSON result](/img/tutorials/triggers/deserialize-json-result.png)

  </Step>
</Steps>
